package com.gitee.huanminabc.jlambda_sql.warapper;

import com.gitee.huanminabc.jcommon.model.DataKeyValue;
import com.gitee.huanminabc.jcommon.reflect.AnnotationUtil;
import com.gitee.huanminabc.jcommon.reflect.FieldUtil;
import com.gitee.huanminabc.jcommon.reflect.LambdaUtil;
import com.gitee.huanminabc.jcommon.str.StringUtil;
import com.gitee.huanminabc.jlambda_sql.SQL;
import com.gitee.huanminabc.jlambda_sql.TableInfo;
import com.gitee.huanminabc.jlambda_sql.annotation.TableField;
import com.gitee.huanminabc.jlambda_sql.annotation.TableId;
import com.gitee.huanminabc.jlambda_sql.annotation.TableName;
import com.gitee.huanminabc.jlambda_sql.base.SqlFun;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

@EqualsAndHashCode(callSuper = true)
@Data
public abstract class AbstractSqlWrapper<T, Children extends AbstractSqlWrapper<T, Children>> extends AbstractBaseWrapper<T, Children> {
    protected StringBuilder caseWhen = new StringBuilder();
    //这种是为了支持多表查询
    private final Map<String, TableInfo> entityClassList = new HashMap<>();
    protected final Children typedThis = (Children) this; //对象本身
    //全局别名索引 , 这样就能保证不同的子查询别名不会重复

    private int aliasIndex = 1;//别名索引
    public static String prefixAlias = "t";

    public String getNewAlias() {
        return prefixAlias + aliasIndex++;
    }

    private Class<T> entityClass;
    private TableInfo tableInfo;


    @Override
    public void clear() {
        caseWhen = new StringBuilder();
        SQL().resetSQL(new SQL());
        entityClassList.clear();
        aliasIndex = 1;

    }

    @Override
    public String getSqlSegment() {
        return SQL().toString();
    }


    public AbstractSqlWrapper() {
    }

    public AbstractSqlWrapper(Class<T> entityClass, SQL sql) {
        super(sql);
        this.entityClass = entityClass;
        this.tableInfo = createTableInfo(entityClass);

    }

    protected TableInfo createTableInfo(Class<?> entityClass) {
        TableInfo tableInfo = new TableInfo();
        String tableName = classToTableName(entityClass);
        tableInfo.setEntityClass(entityClass);
        tableInfo.setTableName(tableName);
        tableInfo.setAlias(getNewAlias());
        entityClassList.put(entityClass.getName(), tableInfo);
        return tableInfo;
    }


    protected void addTable(TableInfo tableInfo) {
        entityClassList.put(tableInfo.getEntityClass().getName(), tableInfo);
    }

    protected void addTables(Map<String, TableInfo> tableInfoMap) {
        entityClassList.putAll(tableInfoMap);
    }


    //通过Class获取表名
    protected String classToTableName(Class<?> clazz) {
        //识别注解上的@TableName
        TableName annotation = clazz.getAnnotation(TableName.class);
        //如果没有注解则使用类名
        if (annotation != null) {
            //获取表名
            return annotation.value();
        } else {
            String simpleName = clazz.getSimpleName();
            //首字母小写 Aa -> aa
            String tableName = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
            //将驼峰转为下划线 aBc -> a_bc
            tableName = StringUtil.toUnderline(tableName);
            return tableName;
        }
    }

    //加param
    public Children V(Object param) {
        SQL().append(" ? ");
        SQL().addParam(param);
        return typedThis;
    }

    protected List<String> getColumnAll() {
        List<String> list = new ArrayList<>();
        //获取当前类的所有字段
        Class<?> entityClass = getTableInfo().getEntityClass();
        DataKeyValue<Field, TableId> dataKeyValue = AnnotationUtil.getAnnotationsField(entityClass, TableId.class);
        if (dataKeyValue != null) {
            //获取主键
            TableId annotation = dataKeyValue.getValue();
            if (annotation != null && StringUtil.isNotEmpty(annotation.value())) {
                list.add(annotation.value());
            } else {
                list.add(dataKeyValue.getKey().getName());
            }
        }
        //只获取可访问的方法
        Map<Field, TableField> annotationsFieldMap = AnnotationUtil.getAnnotationsFieldMap(entityClass, TableField.class);
        for (Map.Entry<Field, TableField> fieldTableFieldEntry : annotationsFieldMap.entrySet()) {
            Field field = fieldTableFieldEntry.getKey();
            //排除注解上是忽略的@TableField(exist = false)
            TableField annotation = field.getAnnotation(TableField.class);
            if (annotation == null) {
                continue;
            }
            if (annotation.value() == null) {
                list.add(field.getName());
            } else {
                list.add(annotation.value());
            }
        }
        return list;
    }


    //处理lambda表达式字段
    protected <Y> void getFunFieldName(SqlFun<Y, ?> column, Consumer<SQL.SqlColumn> consumer) {
        getFunFieldName(column, consumer, true);
    }

    protected <Y> void getFunFieldName(SqlFun<Y, ?> column, Consumer<SQL.SqlColumn> consumer, boolean isExist) {
        Map<String, Object> stringObjectMap = LambdaUtil.lambdaInvocation(column);
        String field = stringObjectMap.get("fieldName").toString();
        Class<?> entityClass = (Class<?>) stringObjectMap.get("clazz");
        //因为基于mybaitis plus的原因,还需要判断字段上有TableField注解那么就取这个注解的值
        //这个注解是为了支持数据库字段和实体类字段不一致的情况

        Field field1 = FieldUtil.getField(entityClass, field);
        TableField annotation = field1.getAnnotation(TableField.class);
        if (annotation != null && StringUtil.isNotEmpty(annotation.value())) {
            field = annotation.value();
        }
        //判断是不是当前类
        if (entityClass.equals(getTableInfo().getEntityClass())) {
            consumer.accept(new SQL.SqlColumn(getTableInfo().getAlias(), field));
        } else {
            TableInfo tableInfo = getEntityClassList().get(entityClass.getName());
            //如果是空那么就表示没有加入此类表,那么就加入
            if (tableInfo == null && isExist) {
                tableInfo = this.createTableInfo(entityClass);
            } else if (tableInfo == null) {
                throw new RuntimeException(entityClass.getName() + "没有加入到语句中,请检查语法是否正确");
            }
            consumer.accept(new SQL.SqlColumn(tableInfo.getAlias(), field));
        }
    }

    @Override
    public String toString() {
        return SQL().toString();
    }
}
